﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace matrixdotnet.CommonDAL
{
    public class DataEntityDirtyEventArgs : EventArgs
    {
        public DataEntity DirtyEntity { get; protected set; }
        public DataEntityDirtyEventArgs(DataEntity dirtyEntity)
        {
            DirtyEntity = dirtyEntity;
        }
    }

    public class DataEntityDeletedEventArgs : EventArgs
    {
        public DataEntity DeletedEntity { get; protected set; }
        public DataEntityDeletedEventArgs(DataEntity deletedEntity)
        {
            DeletedEntity = deletedEntity;
        }
    }

    public class DataEntityConstraintIntegrityEventArgs : EventArgs
    {
        public DataEntity Sender { get; protected set; }
        public DataEntity ConstraintEntity { get; protected set; }
        public DataEntityConstraintIntegrityEventArgs(DataEntity sender, DataEntity constraintEntity)
        {
            Sender = sender;
            ConstraintEntity = constraintEntity;
        }
    }

    public abstract class DataEntity : IDataEntity
    {
        public abstract DataEntityState EntityState
        {
            get;
            protected set;
        }

        public event EventHandler<DataEntityDirtyEventArgs> Dirty;
        public event EventHandler<DataEntityDeletedEventArgs> Deleted;
        public event EventHandler<DataEntityConstraintIntegrityEventArgs> ConstraintIntegrity;

        public int Create()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {
                case DataEntityState.Added:
                    OnConstraintIntegrity(ConstraintIntegrityEntitiesOnCreate);
                    int rowsAffected = CreateEntity();
                    EntityState = DataEntityState.Unchanged;
                    return rowsAffected;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for operation 'Create'", entityState));
            }
        }

        public int Update()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {
                case DataEntityState.Unchanged:
                    return 1;

                case DataEntityState.Modified:
                    OnConstraintIntegrity(ConstraintIntegrityEntitiesOnUpdate);
                    int rowsAffected = UpdateEntity();
                    EntityState = DataEntityState.Unchanged;
                    return rowsAffected;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for operation 'Update'", entityState));
            }
        }

        public void MarkDeleted()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {

                case DataEntityState.Unchanged:
                case DataEntityState.Added:
                case DataEntityState.Modified:
                    EntityState = DataEntityState.Deleted;
                    if (Deleted != null)
                        Deleted(this, new DataEntityDeletedEventArgs(this));

                    break;

                case DataEntityState.Deleted:
                    break;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for being marked as deleted", entityState));
            }
        }

        public int Delete()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {
                case DataEntityState.Deleted:
                    OnConstraintIntegrity(ConstraintIntegrityEntitiesOnDelete);
                    int rowsAffected = DeleteEntity();
                    EntityState = DataEntityState.None;
                    return rowsAffected;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for operation 'Delete'", entityState));
            }
        }

        protected virtual void OnDirty()
        {
            switch (EntityState)
            {
                case DataEntityState.Unchanged:
                    EntityState = DataEntityState.Modified;
                    if (Dirty != null)
                        Dirty(this, new DataEntityDirtyEventArgs(this));

                    break;

                default:
                    break;
            }
        }

        protected virtual void OnConstraintIntegrity(IEnumerable<DataEntity> constraintEntities)
        {
            foreach (var entity in constraintEntities)
                OnConstraintIntegrity(entity);
        }

        protected virtual void OnConstraintIntegrity(DataEntity constraintEntity)
        {
            if (ConstraintIntegrity != null)
                ConstraintIntegrity(this, new DataEntityConstraintIntegrityEventArgs(this, constraintEntity));
        }

        protected abstract IEnumerable<DataEntity> ConstraintIntegrityEntitiesOnCreate { get; }
        protected abstract IEnumerable<DataEntity> ConstraintIntegrityEntitiesOnUpdate { get; }
        protected abstract IEnumerable<DataEntity> ConstraintIntegrityEntitiesOnDelete { get; }
        protected abstract int CreateEntity();
        protected abstract int UpdateEntity();
        protected abstract int DeleteEntity();
    }

    /*
    public abstract class DataEntity : INotifyPropertyChanged
    {
        private Dictionary<DataEntityState, bool> _notifyPropertyChangedRules;
        private DataEntityState _entityState;

        public IDictionary<DataEntityState, bool> NotifyPropertyChangedRules
        {
            get
            {
                return _notifyPropertyChangedRules;
            }
        }
        public DataEntityState EntityState 
        {
            get
            {
                return _entityState;
            }

            protected set
            {
                _entityState = value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public event EventHandler<DataEntityDirtyEventArgs> Dirty;
        public event EventHandler<DataEntityDeletedEventArgs> Deleted;
        public event EventHandler<DataEntityConstraintIntegrityEventArgs> ConstraintIntegrity;

        public DataEntity(DataEntityState entityState)
        {
            EntityState = entityState;

            _notifyPropertyChangedRules = new Dictionary<DataEntityState, bool>();

            NotifyPropertyChangedRules.Add(DataEntityState.None, false);
            NotifyPropertyChangedRules.Add(DataEntityState.Unchanged, false);
            NotifyPropertyChangedRules.Add(DataEntityState.Added, false);
            NotifyPropertyChangedRules.Add(DataEntityState.Modified, false);
            NotifyPropertyChangedRules.Add(DataEntityState.Deleted, false);
        }

        public int Create()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {
                case DataEntityState.Added:
                    OnConstraintIntegrity(ConstraintIntegrityEntitiesOnCreate);
                    int rowsAffected = CreateEntity();
                    EntityState = DataEntityState.Unchanged;
                    return rowsAffected;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for operation 'Create'", entityState));
            }
        }

        public int Update()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {
                case DataEntityState.Unchanged:
                    return 1;

                case DataEntityState.Modified:
                    OnConstraintIntegrity(ConstraintIntegrityEntitiesOnUpdate);
                    int rowsAffected = UpdateEntity();
                    EntityState = DataEntityState.Unchanged;
                    return rowsAffected;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for operation 'Update'", entityState));
            }
        }

        public void MarkDeleted()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {

                case DataEntityState.Unchanged:
                case DataEntityState.Added:
                case DataEntityState.Modified:
                    EntityState = DataEntityState.Deleted;
                    if (Deleted != null)
                        Deleted(this, new DataEntityDeletedEventArgs(this));

                    break;

                case DataEntityState.Deleted:
                    break;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for being marked as deleted", entityState));
            }
        }

        public int Delete()
        {
            DataEntityState entityState = EntityState;
            switch (entityState)
            {
                case DataEntityState.Deleted:
                    OnConstraintIntegrity(ConstraintIntegrityEntitiesOnDelete);
                    int rowsAffected = DeleteEntity();
                    EntityState = DataEntityState.None;
                    return rowsAffected;

                default:
                    throw new ApplicationException(string.Format(
                        "The data entity is in improper state '{0}' for operation 'Delete'", entityState));
            }
        }


        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null && NotifyPropertyChangedRules[EntityState])
                PropertyChanged(this, e);

            OnDirty();
        }

        protected virtual void OnDirty()
        {
            switch (EntityState)
            {
                case DataEntityState.Unchanged:
                    EntityState = DataEntityState.Modified;
                    if (Dirty != null)
                        Dirty(this, new DataEntityDirtyEventArgs(this));

                    break;

                default:
                    break;
            }
        }

        protected virtual void OnConstraintIntegrity(IEnumerable<DataEntity> constraintEntities)
        {
            foreach (var entity in constraintEntities)
                OnConstraintIntegrity(entity);
        }

        protected virtual void OnConstraintIntegrity(DataEntity constraintEntity)
        {
            if (ConstraintIntegrity != null)
                ConstraintIntegrity(this, new DataEntityConstraintIntegrityEventArgs(this, constraintEntity));
        }

        protected abstract IEnumerable<DataEntity> ConstraintIntegrityEntitiesOnCreate { get; }
        protected abstract IEnumerable<DataEntity> ConstraintIntegrityEntitiesOnUpdate { get; }
        protected abstract IEnumerable<DataEntity> ConstraintIntegrityEntitiesOnDelete { get; }
        protected abstract int CreateEntity();
        protected abstract int UpdateEntity();
        protected abstract int DeleteEntity();
    }

    public abstract class DataEntity<TIdentity>
    {
        public TIdentity Identity { get; protected set; }

        public DataEntity()
        {
        }

        public DataEntity(TIdentity identity)
        {
            if (identity.Equals(default(TIdentity)))
                throw new ArgumentNullException("identity");

            Identity = identity;
        }
    }

     * */

}


